
/**************************************************************************
*
*		Object Name: SliderCellFine
*
*--------------------------------------------------------------------------
*		Programmer:andrew stone
*		Copyright (c) 1989,1990 Stone Design Corp.  All rights reserved. 
***************************************************************************/

#import "SliderCellFine.h"
#import "SliderDualActing.h"
#import <appkit/Application.h>
#import <dpsclient/wraps.h>
#import <math.h>


/* Dragging 'resolution' of slider: how much the slider changes by */

#define FINE	.5				/* reduce altStep to 50% */
#define SUPERFINE	.25			/* reduce altStep to 25% */
#define DEFAULT_VAL	50.
#define DEFAULT_MAX	100.
#define DEFAULT_MIN	0.
#define DEFAULT_ALT	1.

@implementation SliderCellFine

+ new
{
	//sane defaults
	self = [super new];
	[self setAltStep:DEFAULT_ALT whole:YES default:DEFAULT_VAL];
	[self setMax:DEFAULT_MAX allowHigher:YES min:DEFAULT_MIN allowLower:YES];
	return self;
}

- increment
{
	if (value+altStep <= maxValue) value += altStep;
	else if (scfFlags.allowHigher)
		{ [self setMaxValue:value+altStep]; value += altStep; }
	else return nil;	// failed to work
	
	[textPal setFloatValue:value];
	return self;
}
- decrement
{
	if (value-altStep >= minValue) value -= altStep;
	else if (scfFlags.allowLower)
		 { [self setMinValue:value+altStep]; value -= altStep; }
	else return nil;	// failed to work

	[textPal setFloatValue:value];
	return self;
}


- (double)checkValue:(double)val     // returns validated number
{
   if (val>= minValue && val<= maxValue) {
      value = val;
      return val;
   } else if (val<minValue && scfFlags.allowLower) {
      [self setMinValue:val];
      value = val;
      return val;
   } else if (val>maxValue && scfFlags.allowHigher) {
       [self setMaxValue:val];
       value = val;
       return val;
    } else return value;      // no change allowed
}

-(BOOL)isDecimal
{ 
  return scfFlags.isDecimal;
}

- (BOOL)continueTracking:(const NXPoint *)lastPoint
		      at:(const NXPoint *)currentPoint
		      inView:controlView
{
    NXEvent *e = [NXApp currentEvent];
 
    if  (e->flags & NX_ALTERNATEMASK) {
        NXRect r;
		double step = altStep;
	
		if (!lastPoint) lastPoint = currentPoint;
	
		if (e->flags & NX_SHIFTMASK) {
	  		if (textPal) {
	  			[textPal setFloatingPointFormat:YES left:2 right:2];
				scfFlags.isDecimal =YES;
	    	}
	    	if (e->flags & NX_COMMANDMASK) step*=SUPERFINE; 
	    	else step *= FINE;		 
		}
		else if (scfFlags.isWhole) value = floor(value);


        if (trackRect.size.width>trackRect.size.height) {
	    	[self getKnobRect:&r flipped:NO];
	    	if (r.origin.x+r.size.width<trackRect.size.width)
				r.size.width+=1.;
	   		if (currentPoint->x >lastPoint->x) {
	   			value+=step;if (value>maxValue) value=maxValue;
	    	} else if (currentPoint->x < lastPoint->x) {
	   	 		value -= step;if (value<minValue)value=minValue;
            }
		} else  { 				// is a vertical slider
	    	[self getKnobRect:&r flipped:YES];
	    	if (r.origin.y+r.size.height<trackRect.size.height)
				r.size.height+=1.;
	    	if (currentPoint->y < lastPoint->y) {
				value+=step; if (value>maxValue) value=maxValue;
	    	} else if (currentPoint->y > lastPoint->y) {
				value -= step; if (value<minValue)value=minValue;
	    	}
		}
		PSsetgray(.5);		// Track gray 1.0
		NXRectFill(&r);
		[self drawKnob];
		
    	[textPal setFloatValue:value];
		if (scfFlags.sendContinuously) [controlView _sendIt];
 
		lastPoint = currentPoint;
		return YES;
    } 
    else if  (e->flags & NX_COMMANDMASK) {
		NXRect r;
		if (trackRect.size.width>trackRect.size.height)
			[self getKnobRect:&r flipped:NO];
		else 
			[self getKnobRect:&r flipped:YES];
		value = defaultValue;
		if (textPal) {
			[textPal setFloatingPointFormat:NO left:4 right:0];
			[textPal setFloatValue:value];
			scfFlags.isDecimal = NO;
		}
		if (defaultMax) [self setMaxValue:defaultMax];
		if (defaultMin) [self setMinValue:defaultMin];
		PSsetgray(.5);
		NXRectFill(&r);
        [self drawKnob];
        return YES;
    } else {
    	BOOL retVal;
  		[textPal setFloatValue:value];
     	retVal =[super continueTracking:(const NXPoint *)lastPoint
		      at:(const NXPoint *)currentPoint
		      inView:controlView];
  		[textPal setFloatValue:value];  //YUP, two times is better than 1!
		if (scfFlags.sendContinuously) [controlView _sendIt];
		return retVal;
     }
}

 // Client Initialization Routines:

 // this method allows slider to setup what fine step will be
 // and if we should keep it to a whole integer

- setAltStep:(double)step whole:(BOOL)flag default:(double)val
{
	scfFlags.isWhole = flag;
	scfFlags.isDecimal = 1-flag;     // support for Clients rounding results
	if (scfFlags.isWhole) [textPal setFloatingPointFormat:NO left:4 right:0]; 
	altStep = step;
	defaultValue = val;
	value = val;
	return self;
}

 // set highest high and yes if you can surpass and lowest lows and surpass flag

- setMax:(double)max allowHigher:(BOOL)hi min:(double)min allowLower:(BOOL)lo
{
	defaultMax = max;
	[self setMaxValue:max];
	defaultMin = min;
	[self setMinValue:min];
	scfFlags.allowHigher = hi;
	scfFlags.allowLower = lo;
	return self;
}

 // archive methods:   // not tested C.E.
- read:(NXTypedStream *)stream
{
	[super read:stream];
	NXReadTypes(stream,"sffff",&scfFlags,&altStep,
									&defaultValue,&defaultMax,&defaultMin);
    [self setMinValue:defaultMin];
  	[self setMaxValue:defaultMax];
	return self;
}

- write:(NXTypedStream *)stream
{
	[super write:stream];
	NXWriteTypes(stream,"sffff",&scfFlags,&altStep,
									&defaultValue,&defaultMax,&defaultMin);
	return self;
}

 // private and historical functions

- _setAlwaysSendUpAction:(BOOL)flag
{
 	scfFlags.sendContinuously = flag;
	return self;
}

- setDefault:(double) def
{
   defaultValue = def;   // needed for GrayView
   return self;
}

- setTextPal:anObject
{
  textPal = anObject;
  return self;
}

@end
